// Copyright (c) 2009 - Quildreen <quildreen@gmail.com>                     
//                                                                          
//  Permission is hereby granted, free of charge, to any person              
//  obtaining a copy of this software and associated documentation files     
//  (the "Software"), to deal in the Software without restriction,           
//  including without limitation the rights to use, copy, modify, merge,     
//  publish, distribute, sublicense, and/or sell copies of the Software,     
//  and to permit persons to whom the Software is furnished to do so,        
//  subject to the following conditions:                                     
//                                                                           
//  The above copyright notice and this permission notice shall be           
//  included in all copies or substantial portions of the Software.          
//                                                                           
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,          
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF       
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                    
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE   
//  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION   
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION    
//  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

/**
 * lily.graphics.dom.core
 * 
 * Abstracts graphics rendering in javascript.
 * 
 * Requires: `graphics.core`
 **/
lily.require("graphics.core");
lily.onLoad(function() {

    lily.defineModule("graphics.dom.core");


    // get which one is available
    (function()
    {
        var prefixes = ['O', 'Moz', 'Webkit', 'KHTML', 'MS'],
            p,
            elm = new Element("div");

        function getProp(name) {
            var candidates = prefixes.concat(),
                cname = name.capitalize();

            while ((p = candidates.shift())) {
                if (typeof elm.style[p + cname] != "undefined") {
                    return p + cname;
                }
            }
            return name;
        }

        /**
         * lily.graphics.dom.transform -> String
         * 
         * Defines which transform property to use.
         **/
        lily.graphics.dom.transform = getProp("transform");
    }());



    /**
     * class lily.graphics.dom.Surface < lily.graphics.Surface
     * 
     * Expands over the abstract Surface using DOM rendering.
     **/
    lily.graphics.dom.Surface = Class.create(lily.graphics.Surface,
    {
        /**
         * lily.graphics.dom.Surface.supported_images -> Array
         * 
         * List of images supported by this object.
         **/
        supported_images: ['.jpg',
                           '.png',
                           '.gif',
                           '.bmp',
                           '.jpeg'],


        /**
         * new lily.graphics.dom.Surface([url][, width = 0][, height = 0])
         * - url (String): optional url with the image to load.
         * - width (Number): width of the Surface's canvas.
         * - height (Number): height of the Surface's canvas.
         **/
        initialize: function($super, url, width, height)
        {
            $super(width, height);

            /**
             * lily.graphics.dom.Surface#_canvas -> HTMLElement
             * 
             * Underlaying DOM HTMLElement to be used as Surface.
             **/
            this._canvas = new Element("div");
            this._canvas.identify();

            /**
             * lily.graphics.dom.Surface#_pending_nodes -> DocumentFragment
             * 
             * A document fragment to store nodes we should append to the
             * surface in the next iteration.
             **/
            this._pending_nodes = document.createDocumentFragment();

            // initializes the surface...
            this._canvas.setStyle({
                position: 'absolute',
                overflow: 'hidden',
                top: this.y,
                left: this.x,
                width: (width || 0) + "px",
                height: (height || 0) + "px"
            }).setOpacity(this.opacity);

            if (url) {
                this.load(url);
            }
        },


        /**
         * lily.graphics.dom.Surface#blit([surface][, position]) -> undefined
         * - surface (Surface): the surface to blit
         * - position (Array): optional `[x, y]` array.
         * 
         * Blits another surface into this one.
         * 
         * If `surface` is not given, we'll just update this Surface instead.
         * 
         * If `position` is not given, the position where to blit the
         * surface will be taken from the Surface instance.
         **/
        blit: function(surface, position)
        {
            surface || (surface = this);
            position || (position = [surface.x, surface.y]);
            var id = surface.id;

            // test if any changes
            if ((this._childs[id] == position || surface == this) &&
                surface._change_list == {}) {
                return;
            }

            // String of 2d transforms
            var transform = "";
            var style = surface._canvas.style;
            var change_list = surface._change_list;

            style.display = "none";

            // Inserts into the surface, if it's not there yet
            if (surface != this && !this._childs[id]) {
                this._canvas.insert(surface._canvas);
            }

            // sets position
            if (position != this._childs[id]) {
                style.left = position[0] + "px";
                style.top = position[1] + "px";
                this._childs[id] = position;
            }

            // apply scale
            var scalex = change_list['scalex'];
            if (change_list['scale']) {
                transform += " scale(" + surface.scale_x + ", "
                                       + surface.scale_y + ")";
            }

            // apply rotation
            if (change_list['rotation']) {
                transform += " rotate(" + surface.angle + "deg)";
            }

            // apply opacity
            if (change_list['opacity']) {
                surface._canvas.setOpacity(opacity);
            }

            // set transforms
            if (transform) {
                style[lily.graphics.dom.transform] = transform;
            }

            // set size
            if (change_list['size']) {
                style.width = surface.width + "px";
                style.height = surface.height + "px";
            }

            // applies changes and clear change buffer
            if (change_list['zindex']) {
                 style["z-index"] = surface.z;
            }

            if (change_list['fill']) {
                surface._canvas.appendChild(this._pending_nodes);
                this._prending_nodes = document.createDocumentFragment();
            }

            style.display = "block";
            surface._change_list = {};
        },


        /**
         * lily.graphics.dom.Surface#scroll([x = 0][, y = 0]) -> lily.graphics.Surface
         * 
         * Scrolls the contents of the surface.
         **/
        scroll: function(x, y)
        {
            this._canvas.style.backgroundPosition = (-x || 0) + "px " +
                                                    (-y || 0) + "px";
        },


        /**
         * lily.graphics.dom.Surface#fill(colour[, rect]) -> lily.graphics.Surface
         * - colour (String): CSS-style colour to use.
         * - rect (Object): optional rect of the are to fill.
         * 
         * Fills the surface (or a rectangle of this surface) with a solid
         * colour.
         * 
         * The optional `rect` argument should be an object defining the
         * properties `left`, `right`, `top` and `bottom` (the boundaries of
         * the rectangle).
         * 
         * If a `rect` argument is not given, the entire surface will be
         * filled.
         **/
        fill: function(colour, rect)
        {
            rect || (rect = {
                left: 0,
                top: 0,
                right: this.width,
                bottom: this.height
            });
            this._pending_nodes.appendChild(new Element("div").setStyle({
                position: 'absolute',
                overflow: 'hidden',
                backgroundColor: colour, /* silly CSS again */
                top: rect.top + "px",
                left: rect.left + "px",
                width: (rect.right - rect.left) + "px",
               height: (rect.bottom - rect.top) + "px"
            }));
            this._change_list['fill'] = true;
            return this;
        },


        /**
         * lily.graphics.dom.Surface#clear() -> lily.graphics.dom.Surface
         * 
         * Clears the contents of the surface.
         **/
        clear: function()
        {
            return this._canvas.update("");
        },

        /**
         * lily.graphics.dom.Surface#load(url) -> lily.graphics.dom.Surface
         * - url (String): url of the resource to load.
         * 
         * Loads an image or content from the given resource, and paints
         * it on the surface.
         **/
        load: function(url)
        {
            var img;
            var i = this.supported_images.length;

            while (i--) {
                if (url.endsWith(this.supported_images[i])) {
                    this._canvas.setStyle({backgroundImage: 'url(' + url + ')',
                                           backgroundRepeat: 'no-repeat'
                    });

                    var surf = this;

                    img = new Image();
                    img.onload = function() {
                        if (!surf.width || !surf.height) {
                            surf.setSize(this.width, this.height);
                        }
                    };
                    img.src = url;

                    return this;
                }
            }

            if (!img) {
                throw new Error("Couldn't load resource at " + url);
            }
        },


        /**
         * lily.graphics.dom.Surface#getStyle() -> String | null
         * 
         * Returns the CSS style for the surface.
         **/
        getStyle: function()
        {
            return this._canvas.getStyle();
        },


        /**
         * lily.graphics.dom.Surface#setStyle() -> lily.graphics.dom.Surface
         * - styles (String | Object): the CSS rules to apply
         * 
         * Sets the CSS style for the surface.
         **/
        setStyle: function(styles)
        {
            this._canvas.setStyle(styles);
            return this;
        }
    });
}); // onLoad